package com.dbh.alg.algorithm.hot100.二分查找;

/**
 * 整数数组 nums 按升序排列，数组中的值 互不相同 。
 * <p>
 * 在传递给函数之前，nums 在预先未知的某个下标 k（0 <= k < nums.length）上进行了 向左旋转，使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]（下标 从 0 开始 计数）。例如， [0,1,2,4,5,6,7] 下标 3 上向左旋转后可能变为 [4,5,6,7,0,1,2] 。
 * <p>
 * 给你 旋转后 的数组 nums 和一个整数 target ，如果 nums 中存在这个目标值 target ，则返回它的下标，否则返回 -1 。
 * <p>
 * 你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
 * <p>
 *
 *
 * 示例 1：
 * <p>
 * 输入：nums = [4,5,6,7,0,1,2], target = 0
 * 输出：4
 * 示例 2：
 * <p>
 * 输入：nums = [4,5,6,7,0,1,2], target = 3
 * 输出：-1
 * 示例 3：
 * <p>
 * 输入：nums = [1], target = 0
 * 输出：-1
 * <p>
 *
 * 提示：
 * <p>
 * 1 <= nums.length <= 5000
 * -104 <= nums[i] <= 104
 * nums 中的每个值都 独一无二
 * 题目数据保证 nums 在预先未知的某个下标上进行了旋转
 * -104 <= target <= 104
 */
public class Leetcode33_搜索旋转排序数组 {

    /**
     * 在旋转有序数组中搜索目标值
     * @param nums 旋转后的有序数组（元素唯一）
     * @param target 要搜索的目标值
     * @return 目标值在数组中的索引，如果不存在则返回-1
     */
    public int search(int[] nums, int target) {
        // 处理空数组的情况
        if (nums == null || nums.length == 0) {
            return -1;
        }

        // 初始化搜索范围：整个数组
        int left = 0, right = nums.length - 1;

        // 二分查找循环
        while (left <= right) {
            // 计算中间位置，使用无符号右移避免溢出
            // 等同于 (left + right) / 2，但避免溢出问题
            int mid = (left + right) >>> 1;

            // 如果中间元素正好是目标值，直接返回索引
            if (nums[mid] == target) {
                return mid;
            }
            // 判断中间元素位于旋转后的哪一部分
            if (nums[mid] >= nums[left]) {
                // 情况1：中间元素在左侧有序部分,
                // 因为分成两部分，左侧部分数据都比右侧部分数据大，所以nums[mid] >= nums[left],mid还是落在左侧部分
                if (target >= nums[left] && target < nums[mid]) {
                    // 缩小搜索范围到左半部分，那么target在left...mid之间
                    right = mid - 1;
                } else {
                    // 否则目标值可能在右半部分
                    left = mid + 1;
                }
            } else {
                // 情况2：中间元素在右侧有序部分
                if (target > nums[mid] && target <= nums[right]) {
                    // 如果目标值在右侧有序部分的范围内，那么target在mid...right之间
                    // 缩小搜索范围到右半部分
                    left = mid + 1;
                } else {
                    // 否则目标值可能在左半部分
                    right = mid - 1;
                }
            }
        }
        return -1;
    }

}
